/**
*  deferredreturn.h
*
*  @copyright 2016
*/

/**
*  Include guard
*/
#pragma once

/**
*  Dependencies
*/
#include "deferredmessage.h"

/**
*  Start namespace
*/
namespace AMQP {

	/**
	*  Forward declarations
	*/
	class BasicReturnFrame;

	/**
	*  Base class for deferred return
	*/
	class DeferredReturn :
		public DeferredMessage,
		public std::enable_shared_from_this<DeferredReturn>
	{
	private:
		/**
		*  Process a return frame
		*
		*  @param  frame   The frame to process
		*/
		void process(BasicReturnFrame &frame);

		/**
		*  Indicate that a message was done
		*/
		virtual void complete();

		/**
		*  Emit a message
		*
		*  @param  message     The message to emit
		*  @param  deliveryTag The delivery tag (for ack()ing)
		*  @param  redelivered Is this a redelivered message
		*/
		void emit(Message &&message, int16_t replyCode, const std::string& replyText) const;

		/**
		*  Frames may be processed
		*/
		friend class ChannelImpl;
		friend class Channel;
		friend class BasicReturnFrame;
	protected:
		/**
		*  reply code
		*  @var int16_t
		*/
		int16_t _replyCode;

		/**
		*  reply text
		*  @var ShortString
		*/
		ShortString _replyText;

		/**
		*  The channel to which the consumer is linked
		*  @var    ChannelImpl
		*/
		std::weak_ptr<ChannelImpl> _channel;

		/**
		*  Callback for new message
		*  @var    BeginCallback
		*/
		BeginCallback _beginCallback;

		/**
		*  Callback for when a message was complete finished
		*  @var    CompleteCallback
		*/
		RetCompleteCallback _completeCallback;

		/**
		*  Callback for return messages
		*  @var    ReturnCallback
		*/
		ReturnCallback	_returnCallback;

		/**
		*  Callback for ack or nack
		*  @var    AckCallback
		*/
		AckCallback		_ackCallback;

		/**
		*  Constructor
		*
		*  @param  failed  Have we already failed?
		*  @param  channel The channel we are returning on
		*/
		DeferredReturn(bool failed, std::weak_ptr<ChannelImpl> channel) : DeferredMessage(failed), _channel(channel) {}
	public:
		/**
		*  Register the function to be called when a new message is expected
		*
		*  @param  callback    The callback to invoke
		*  @return Same object for chaining
		*/
		DeferredReturn &onBegin(const BeginCallback &callback)
		{
			// store callback
			_beginCallback = callback;

			// allow chaining
			return *this;
		}

		/**
		*  Register the function to be called when message headers come in
		*
		*  @param  callback    The callback to invoke for message headers
		*  @return Same object for chaining
		*/
		DeferredReturn &onHeaders(const HeaderCallback &callback)
		{
			// store callback
			_headerCallback = callback;

			// allow chaining
			return *this;
		}

		/**
		*  Register the function to be called when a chunk of data comes in
		*
		*  Note that this function may be called zero, one or multiple times
		*  for each incoming message depending on the size of the message data.
		*
		*  If you install this callback you very likely also want to install
		*  the onComplete callback so you know when the last data part was
		*  received.
		*
		*  @param  callback    The callback to invoke for chunks of message data
		*  @return Same object for chaining
		*/
		DeferredReturn &onData(const DataCallback &callback)
		{
			// store callback
			_dataCallback = callback;

			// allow chaining
			return *this;
		}

		/**
		*  Register the function that is called when the consumer starts
		*  @param  callback
		*/
		DeferredReturn &onSuccess(const SuccessCallback &callback)
		{
			// call base
			Deferred::onSuccess(callback);

			// allow chaining
			return *this;
		}

		/**
		*  Register a function to be called when a message return
		*  
		*  @param  callback    the callback to execute
		*/
		DeferredReturn &onReceived(const ReturnCallback &callback)
		{
			// store callback
			_returnCallback = callback;

			// allow chaining
			return *this;
		}

		/**
		*  Register a function to be called when a message was completely received
		*
		*  @param  callback    The callback to invoke
		*  @return Same object for chaining
		*/
		DeferredReturn &onComplete(const RetCompleteCallback &callback)
		{
			// store callback
			_completeCallback = callback;

			// allow chaining
			return *this;
		}
	};

	/**
	*  End namespace
	*/
}
